home *** CD-ROM | disk | FTP | other *** search
/ Ultra Pack / UltraComputing Partner Applications.iso / SunLabs / tclTK / src / tk4.0 / tkFocus.c < prev    next >
C/C++ Source or Header  |  1995-06-06  |  24KB  |  801 lines

  1. /* 
  2.  * tkFocus.c --
  3.  *
  4.  *    This file contains procedures that manage the input
  5.  *    focus for Tk.
  6.  *
  7.  * Copyright (c) 1990-1994 The Regents of the University of California.
  8.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  */
  13.  
  14. static char sccsid[] = "@(#) tkFocus.c 1.23 95/06/05 21:46:42";
  15.  
  16. #include "tkInt.h"
  17. #include "tkPort.h"
  18.  
  19. /*
  20.  * For each top-level window that has ever received the focus, there
  21.  * is a record of the following type:
  22.  */
  23.  
  24. typedef struct TkFocusInfo {
  25.     TkWindow *topLevelPtr;    /* Information about top-level window. */
  26.     TkWindow *focusWinPtr;    /* The next time the focus comes to this
  27.                  * top-level, it will be given to this
  28.                  * window. */
  29.     struct TkFocusInfo *nextPtr;/* Next in list of all focus records for
  30.                  * a given application. */
  31. } FocusInfo;
  32.  
  33. static int focusDebug = 0;
  34.  
  35. /*
  36.  * The following magic value is stored in the "send_event" field of
  37.  * FocusIn and FocusOut events that are generated in this file.  This
  38.  * allows us to separate "real" events coming from the server from
  39.  * those that we generated.
  40.  */
  41.  
  42. #define GENERATED_EVENT_MAGIC ((Bool) 0x547321ac)
  43.  
  44. /*
  45.  * Forward declarations for procedures defined in this file:
  46.  */
  47.  
  48.  
  49. static void        ChangeXFocus _ANSI_ARGS_((TkWindow *topLevelPtr,
  50.                 int focus));
  51. static void        FocusMapProc _ANSI_ARGS_((ClientData clientData,
  52.                 XEvent *eventPtr));
  53. static void        GenerateFocusEvents _ANSI_ARGS_((TkWindow *sourcePtr,
  54.                 TkWindow *destPtr));
  55. static void        SetFocus _ANSI_ARGS_((TkWindow *winPtr, int force));
  56.  
  57. /*
  58.  *--------------------------------------------------------------
  59.  *
  60.  * Tk_FocusCmd --
  61.  *
  62.  *    This procedure is invoked to process the "focus" Tcl command.
  63.  *    See the user documentation for details on what it does.
  64.  *
  65.  * Results:
  66.  *    A standard Tcl result.
  67.  *
  68.  * Side effects:
  69.  *    See the user documentation.
  70.  *
  71.  *--------------------------------------------------------------
  72.  */
  73.  
  74. int
  75. Tk_FocusCmd(clientData, interp, argc, argv)
  76.     ClientData clientData;    /* Main window associated with
  77.                  * interpreter. */
  78.     Tcl_Interp *interp;        /* Current interpreter. */
  79.     int argc;            /* Number of arguments. */
  80.     char **argv;        /* Argument strings. */
  81. {
  82.     Tk_Window tkwin = (Tk_Window) clientData;
  83.     TkWindow *winPtr = (TkWindow *) clientData;
  84.     TkWindow *newPtr, *focusWinPtr, *topLevelPtr;
  85.     FocusInfo *focusPtr;
  86.     char c;
  87.     size_t length;
  88.  
  89.     /*
  90.      * If invoked with no arguments, just return the current focus window.
  91.      */
  92.  
  93.     if (argc == 1) {
  94.     focusWinPtr = TkGetFocus(winPtr);
  95.     if (focusWinPtr != NULL) {
  96.         interp->result = focusWinPtr->pathName;
  97.     }
  98.     return TCL_OK;
  99.     }
  100.  
  101.     /*
  102.      * If invoked with a single argument beginning with "." then focus
  103.      * on that window.
  104.      */
  105.  
  106.     if (argc == 2) {
  107.     if (argv[1][0] == 0) {
  108.         return TCL_OK;
  109.     }
  110.     if (argv[1][0] == '.') {
  111.         newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[1], tkwin);
  112.         if (newPtr == NULL) {
  113.         return TCL_ERROR;
  114.         }
  115.         if (!(newPtr->flags & TK_ALREADY_DEAD)) {
  116.         SetFocus(newPtr, 0);
  117.         }
  118.         return TCL_OK;
  119.     }
  120.     }
  121.  
  122.     length = strlen(argv[1]);
  123.     c = argv[1][1];
  124.     if ((c == 'd') && (strncmp(argv[1], "-displayof", length) == 0)) {
  125.     if (argc != 3) {
  126.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  127.             argv[0], " -displayof window\"", (char *) NULL);
  128.         return TCL_ERROR;
  129.     }
  130.     newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
  131.     if (newPtr == NULL) {
  132.         return TCL_ERROR;
  133.     }
  134.     newPtr = TkGetFocus(newPtr);
  135.     if (newPtr != NULL) {
  136.         interp->result = newPtr->pathName;
  137.     }
  138.     } else if ((c == 'f') && (strncmp(argv[1], "-force", length) == 0)) {
  139.     if (argc != 3) {
  140.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  141.             argv[0], " -force window\"", (char *) NULL);
  142.         return TCL_ERROR;
  143.     }
  144.     if (argv[2][0] == 0) {
  145.         return TCL_OK;
  146.     }
  147.     newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
  148.     if (newPtr == NULL) {
  149.         return TCL_ERROR;
  150.     }
  151.     SetFocus(newPtr, 1);
  152.     } else if ((c == 'l') && (strncmp(argv[1], "-lastfor", length) == 0)) {
  153.     if (argc != 3) {
  154.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  155.             argv[0], " -lastfor window\"", (char *) NULL);
  156.         return TCL_ERROR;
  157.     }
  158.     newPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
  159.     if (newPtr == NULL) {
  160.         return TCL_ERROR;
  161.     }
  162.     for (topLevelPtr = newPtr; topLevelPtr != NULL;
  163.         topLevelPtr = topLevelPtr->parentPtr)  {
  164.         if (topLevelPtr->flags & TK_TOP_LEVEL) {
  165.         for (focusPtr = newPtr->mainPtr->focusPtr; focusPtr != NULL;
  166.             focusPtr = focusPtr->nextPtr) {
  167.             if (focusPtr->topLevelPtr == topLevelPtr) {
  168.             interp->result = focusPtr->focusWinPtr->pathName;
  169.             return TCL_OK;
  170.             }
  171.         }
  172.         interp->result = topLevelPtr->pathName;
  173.         return TCL_OK;
  174.         }
  175.     }
  176.     } else {
  177.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  178.         "\": must be -displayof, -force, or -lastfor", (char *) NULL);
  179.     return TCL_ERROR;
  180.     }
  181.     return TCL_OK;
  182. }
  183.  
  184. /*
  185.  *--------------------------------------------------------------
  186.  *
  187.  * TkFocusFilterEvent --
  188.  *
  189.  *    This procedure is invoked by Tk_HandleEvent when it encounters
  190.  *    a FocusIn, FocusOut, Enter, or Leave event.
  191.  *
  192.  * Results:
  193.  *    A return value of 1 means that Tk_HandleEvent should process
  194.  *    the event normally (i.e. event handlers should be invoked).
  195.  *    A return value of 0 means that this event should be ignored.
  196.  *
  197.  * Side effects:
  198.  *    Additional events may be generated, and the focus may switch.
  199.  *
  200.  *--------------------------------------------------------------
  201.  */
  202.  
  203. int
  204. TkFocusFilterEvent(winPtr, eventPtr)
  205.     TkWindow *winPtr;        /* Window that focus event is directed to. */
  206.     XEvent *eventPtr;        /* FocusIn or FocusOut event. */
  207. {
  208.     /*
  209.      * Design notes: the window manager and X server work together to
  210.      * transfer the focus among top-level windows.  This procedure takes
  211.      * care of transferring the focus from a top-level window to the
  212.      * actual window within that top-level that has the focus.  We
  213.      * do this by synthesizing X events to move the focus around.  None
  214.      * of the FocusIn and FocusOut events generated by X are ever used
  215.      * outside of this procedure;  only the synthesized events get through
  216.      * to the rest of the application.  At one point (e.g. Tk4.0b1) Tk
  217.      * used to call X to move the focus from a top-level to one of its
  218.      * descendants, then just pass through the events generated by X.
  219.      * This approach didn't work very well, for a variety of reasons.
  220.      * For example, if X generates the events they go at the back of
  221.      * the event queue, which could cause problems if other things
  222.      * have already happened, such as moving the focus to yet another
  223.      * window.
  224.      */
  225.  
  226.     FocusInfo *focusPtr;
  227.     TkDisplay *dispPtr = winPtr->dispPtr;
  228.     TkWindow *newFocusPtr;
  229.     int retValue, delta;
  230.  
  231.     /*
  232.      * If this was a generated event, just turn off the generated
  233.      * flag and pass the event through.
  234.      */
  235.  
  236.     if (eventPtr->xfocus.send_event == GENERATED_EVENT_MAGIC) {
  237.     eventPtr->xfocus.send_event = 0;
  238.     return 1;
  239.     }
  240.  
  241.     /*
  242.      * This was not a generated event.  We'll return 1 (so that the
  243.      * event will be processed) if it's an Enter or Leave event, and
  244.      * 0 (so that the event won't be processed) if it's a FocusIn or
  245.      * FocusOut event.  Also, skip NotifyPointer, NotifyPointerRoot,
  246.      * and NotifyInferior focus events immediately; they're not
  247.      * useful and tend to cause confusion.
  248.      */
  249.  
  250.     if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {
  251.     retValue = 0;
  252.     if ((eventPtr->xfocus.detail == NotifyPointer)
  253.         || (eventPtr->xfocus.detail == NotifyPointerRoot)
  254.         || (eventPtr->xfocus.detail == NotifyInferior)) {
  255.         return retValue;
  256.     }
  257.     } else {
  258.     retValue = 1;
  259.     if (eventPtr->xcrossing.detail == NotifyInferior) {
  260.         return retValue;
  261.     }
  262.     }
  263.  
  264.     /*
  265.      * If winPtr isn't a top-level window than just ignore the event.
  266.      */
  267.  
  268.     if (!(winPtr->flags & TK_TOP_LEVEL)) {
  269.     return retValue;
  270.     }
  271.  
  272.     /*
  273.      * If there is a grab in effect and this window is outside the
  274.      * grabbed tree, then ignore the event.
  275.      */
  276.  
  277.     if (TkGrabState(winPtr) == TK_GRAB_EXCLUDED)  {
  278.     return retValue;
  279.     }
  280.  
  281.     /*
  282.      * Find the FocusInfo structure for the window, and make a new one
  283.      * if there isn't one already.
  284.      */
  285.  
  286.     for (focusPtr = winPtr->mainPtr->focusPtr; focusPtr != NULL;
  287.         focusPtr = focusPtr->nextPtr) {
  288.     if (focusPtr->topLevelPtr == winPtr) {
  289.         break;
  290.     }
  291.     }
  292.     if (focusPtr == NULL) {
  293.     focusPtr = (FocusInfo *) ckalloc(sizeof(FocusInfo));
  294.     focusPtr->topLevelPtr = focusPtr->focusWinPtr = winPtr;
  295.     focusPtr->nextPtr = winPtr->mainPtr->focusPtr;
  296.     winPtr->mainPtr->focusPtr = focusPtr;
  297.     }
  298.  
  299.     /*
  300.      * It is possible that there were outstanding FocusIn and FocusOut
  301.      * events on their way to us at the time the focus was changed
  302.      * internally with the "focus" command.  If so, these events could
  303.      * potentially cause us to lose the focus (switch it to the window
  304.      * of the last FocusIn event) even though the focus change occurred
  305.      * after those events.  The following code detects this and puts
  306.      * the focus back to the place where it was rightfully set.
  307.      */
  308.  
  309.     newFocusPtr = focusPtr->focusWinPtr;
  310.     delta = eventPtr->xfocus.serial - winPtr->mainPtr->focusSerial;
  311.     if (focusDebug) {
  312.     printf("check event serial %d, delta %d\n",
  313.         (int) eventPtr->xfocus.serial, delta);
  314.     }
  315.     if ((delta < 0) && (winPtr->mainPtr->lastFocusPtr != NULL)) {
  316.     newFocusPtr = winPtr->mainPtr->lastFocusPtr;
  317.     if (focusDebug) {
  318.         printf("reverting to %s instead of %s\n", newFocusPtr->pathName,
  319.             focusPtr->focusWinPtr->pathName);
  320.     }
  321.     }
  322.  
  323.     if (eventPtr->type == FocusIn) {
  324.     GenerateFocusEvents(dispPtr->focusWinPtr, newFocusPtr);
  325.     dispPtr->focusWinPtr = newFocusPtr;
  326.     dispPtr->implicitWinPtr = NULL;
  327.     if (focusDebug) {
  328.         printf("Focussed on %s\n", newFocusPtr->pathName);
  329.     }
  330.     } else if (eventPtr->type == FocusOut) {
  331.     GenerateFocusEvents(dispPtr->focusWinPtr, (TkWindow *) NULL);
  332.     dispPtr->focusWinPtr = NULL;
  333.     dispPtr->implicitWinPtr = NULL;
  334.     if (focusDebug) {
  335.         printf("Unfocussed from %s, detail %d\n", winPtr->pathName,
  336.             eventPtr->xfocus.detail);
  337.     }
  338.     } else if (eventPtr->type == EnterNotify) {
  339.     /*
  340.      * If there is no window manager, or if the window manager isn't
  341.      * moving the focus around (e.g. the disgusting "NoTitleFocus"
  342.      * option has been selected in twm), then we won't get FocusIn
  343.      * or FocusOut events.  Instead, the "focus" field will be set
  344.      * in an Enter event to indicate that we've already got the focus
  345.      * when then mouse enters the window (even though we didn't get
  346.      * a FocusIn event).  Watch for this and grab the focus when it
  347.      * happens.
  348.      */
  349.  
  350.     if (eventPtr->xcrossing.focus && (dispPtr->focusWinPtr == NULL)) {
  351.         GenerateFocusEvents(dispPtr->focusWinPtr, newFocusPtr);
  352.         dispPtr->focusWinPtr = newFocusPtr;
  353.         dispPtr->implicitWinPtr = winPtr;
  354.         if (focusDebug) {
  355.         printf("Focussed implicitly on %s\n",
  356.             newFocusPtr->pathName);
  357.         }
  358.     }
  359.     } else if (eventPtr->type == LeaveNotify) {
  360.     /*
  361.      * If the pointer just left a window for which we automatically
  362.      * claimed the focus on enter, generate FocusOut events.  Note:
  363.      * dispPtr->implicitWinPtr may not be the same as
  364.      * dispPtr->focusWinPtr (e.g. because the "focus" command was
  365.      * used to redirect the focus after it arrived at
  366.      * dispPtr->implicitWinPtr)!!
  367.      */
  368.  
  369.     if (dispPtr->implicitWinPtr == winPtr) {
  370.         GenerateFocusEvents(dispPtr->focusWinPtr, (TkWindow *) NULL);
  371.         dispPtr->focusWinPtr = NULL;
  372.         dispPtr->implicitWinPtr = NULL;
  373.         if (focusDebug) {
  374.         printf("Defocussed implicitly\n");
  375.         }
  376.     }
  377.     }
  378.     return retValue;
  379. }
  380.  
  381. /*
  382.  *----------------------------------------------------------------------
  383.  *
  384.  * SetFocus --
  385.  *
  386.  *    This procedure is invoked to change the focus window for a
  387.  *    given display in a given application.
  388.  *
  389.  * Results:
  390.  *    None.
  391.  *
  392.  * Side effects:
  393.  *    Event handlers may be invoked to process the change of
  394.  *    focus.
  395.  *
  396.  *----------------------------------------------------------------------
  397.  */
  398.  
  399. static void
  400. SetFocus(winPtr, force)
  401.     TkWindow *winPtr;        /* Window that is to be the new focus for
  402.                  * its display and application. */
  403.     int force;            /* If non-zero, set the X focus to this
  404.                  * window even if the application doesn't
  405.                  * currently have the X focus. */
  406. {
  407.     TkDisplay *dispPtr = winPtr->dispPtr;
  408.     FocusInfo *focusPtr;
  409.     TkWindow *topLevelPtr, *topLevelPtr2;
  410.  
  411.     if (winPtr == dispPtr->focusWinPtr) {
  412.     return;
  413.     }
  414.  
  415.     /*
  416.      * Find the top-level window for winPtr, then find (or create)
  417.      * a record for the top-level.
  418.      */
  419.  
  420.     for (topLevelPtr = winPtr; ; topLevelPtr = topLevelPtr->parentPtr)  {
  421.     if (topLevelPtr == NULL) {
  422.         /*
  423.          * The window is being deleted.  No point in worrying about
  424.          * giving it the focus.
  425.          */
  426.  
  427.         return;
  428.     }
  429.     if (topLevelPtr->flags & TK_TOP_LEVEL) {
  430.         break;
  431.     }
  432.     }
  433.     for (focusPtr = winPtr->mainPtr->focusPtr; focusPtr != NULL;
  434.         focusPtr = focusPtr->nextPtr) {
  435.     if (focusPtr->topLevelPtr == topLevelPtr) {
  436.         break;
  437.     }
  438.     }
  439.     if (focusPtr == NULL) {
  440.     focusPtr = (FocusInfo *) ckalloc(sizeof(FocusInfo));
  441.     focusPtr->topLevelPtr = topLevelPtr;
  442.     focusPtr->nextPtr = winPtr->mainPtr->focusPtr;
  443.     winPtr->mainPtr->focusPtr = focusPtr;
  444.     }
  445.  
  446.     /*
  447.      * Reset the focus, but only if the application already has the
  448.      * input focus or "force" has been specified.
  449.      */
  450.  
  451.     focusPtr->focusWinPtr = winPtr;
  452.     Tk_MakeWindowExist((Tk_Window) winPtr);
  453.     if (force || ((dispPtr->focusWinPtr != NULL)
  454.         && (dispPtr->focusWinPtr->mainPtr == winPtr->mainPtr))) {
  455.     /*
  456.      * Reset the focus in X if it has changed top-levels and if the
  457.      * new top-level isn't override-redirect (the only reason to
  458.      * change the X focus is so that the window manager can redecorate
  459.      * the focus window, but if it's override-redirect then it won't
  460.      * be decorated anyway;  also, changing the focus to menus causes
  461.      * all sorts of problems with olvwm:  the focus gets lost if
  462.      * keyboard traversal is used to move among menus.
  463.      */
  464.  
  465.     if (dispPtr->focusWinPtr != NULL) {
  466.         for (topLevelPtr2 = dispPtr->focusWinPtr;
  467.             (topLevelPtr2 != NULL)
  468.             && !(topLevelPtr2->flags & TK_TOP_LEVEL);
  469.             topLevelPtr2 = topLevelPtr2->parentPtr)  {
  470.         /* Empty loop body. */
  471.         }
  472.     } else {
  473.         topLevelPtr2 = NULL;
  474.     }
  475.     if ((topLevelPtr2 != topLevelPtr)
  476.         && !(topLevelPtr->atts.override_redirect)) {
  477.         if (dispPtr->focusOnMapPtr != NULL) {
  478.         Tk_DeleteEventHandler((Tk_Window) dispPtr->focusOnMapPtr,
  479.             StructureNotifyMask, FocusMapProc,
  480.             (ClientData) dispPtr->focusOnMapPtr);
  481.         dispPtr->focusOnMapPtr = NULL;
  482.         }
  483.         if (topLevelPtr->flags & TK_MAPPED) {
  484.         ChangeXFocus(topLevelPtr, force);
  485.         } else {
  486.         /*
  487.          * The window isn't mapped, so we can't give it the focus
  488.          * right now.  Create an event handler that will give it
  489.          * the focus as soon as it is mapped.
  490.          */
  491.  
  492.         Tk_CreateEventHandler((Tk_Window) topLevelPtr,
  493.             StructureNotifyMask, FocusMapProc,
  494.             (ClientData) topLevelPtr);
  495.         dispPtr->focusOnMapPtr = topLevelPtr;
  496.         dispPtr->forceFocus = force;
  497.         }
  498.     }
  499.     GenerateFocusEvents(dispPtr->focusWinPtr, winPtr);
  500.     dispPtr->focusWinPtr = winPtr;
  501.     }
  502.  
  503.     /*
  504.      * Remember the current serial number for the X server and issue
  505.      * a dummy server request.  This marks the position at which we
  506.      * changed the focus, so we can distinguish FocusIn and FocusOut
  507.      * events on either side of the mark.
  508.      */
  509.  
  510.     winPtr->mainPtr->lastFocusPtr = winPtr;
  511.     winPtr->mainPtr->focusSerial = NextRequest(winPtr->display);
  512.     XNoOp(winPtr->display);
  513.     if (focusDebug) {
  514.     printf("focus marking for %s at %d\n", winPtr->pathName,
  515.         (int) winPtr->mainPtr->focusSerial);
  516.     }
  517. }
  518.  
  519. /*
  520.  *----------------------------------------------------------------------
  521.  *
  522.  * TkGetFocus --
  523.  *
  524.  *    Given a window, this procedure returns the current focus
  525.  *    window for its application and display.
  526.  *
  527.  * Results:
  528.  *    The return value is a pointer to the window that currently
  529.  *    has the input focus for the specified application and
  530.  *    display, or NULL if none.
  531.  *
  532.  * Side effects:
  533.  *    None.
  534.  *
  535.  *----------------------------------------------------------------------
  536.  */
  537.  
  538. TkWindow *
  539. TkGetFocus(winPtr)
  540.     TkWindow *winPtr;        /* Window that selects an application
  541.                  * and a display. */
  542. {
  543.     TkWindow *focusWinPtr;
  544.  
  545.     focusWinPtr = winPtr->dispPtr->focusWinPtr;
  546.     if ((focusWinPtr != NULL) && (focusWinPtr->mainPtr == winPtr->mainPtr)) {
  547.     return focusWinPtr;
  548.     }
  549.     return (TkWindow *) NULL;
  550. }
  551.  
  552. /*
  553.  *----------------------------------------------------------------------
  554.  *
  555.  * TkFocusDeadWindow --
  556.  *
  557.  *    This procedure is invoked when it is determined that
  558.  *    a window is dead.  It cleans up focus-related information
  559.  *    about the window.
  560.  *
  561.  * Results:
  562.  *    None.
  563.  *
  564.  * Side effects:
  565.  *    Various things get cleaned up and recycled.
  566.  *
  567.  *----------------------------------------------------------------------
  568.  */
  569.  
  570. void
  571. TkFocusDeadWindow(winPtr)
  572.     register TkWindow *winPtr;        /* Information about the window
  573.                      * that is being deleted. */
  574. {
  575.     FocusInfo *focusPtr, *prevPtr;
  576.     TkDisplay *dispPtr = winPtr->dispPtr;
  577.  
  578.     /*
  579.      * Search for focus records that refer to this window either as
  580.      * the top-level window or the current focus window.
  581.      */
  582.  
  583.     for (prevPtr = NULL, focusPtr = winPtr->mainPtr->focusPtr;
  584.         focusPtr != NULL;
  585.         prevPtr = focusPtr, focusPtr = focusPtr->nextPtr) {
  586.     if (winPtr == focusPtr->topLevelPtr) {
  587.         /*
  588.          * The top-level window is the one being deleted: free
  589.          * the focus record and release the focus back to PointerRoot
  590.          * if we acquired it implicitly.
  591.          */
  592.  
  593.         if (dispPtr->implicitWinPtr == winPtr) {
  594.         if (focusDebug) {
  595.             printf("releasing focus to root after %s died\n",
  596.                 focusPtr->topLevelPtr->pathName);
  597.         }
  598.         dispPtr->implicitWinPtr = NULL;
  599.         dispPtr->focusWinPtr = NULL;
  600.         }
  601.         if (dispPtr->focusWinPtr == focusPtr->focusWinPtr) {
  602.         dispPtr->focusWinPtr = NULL;
  603.         }
  604.         if (dispPtr->focusOnMapPtr == focusPtr->topLevelPtr) {
  605.         dispPtr->focusOnMapPtr = NULL;
  606.         }
  607.         if (prevPtr == NULL) {
  608.         winPtr->mainPtr->focusPtr = focusPtr->nextPtr;
  609.         } else {
  610.         prevPtr->nextPtr = focusPtr->nextPtr;
  611.         }
  612.         ckfree((char *) focusPtr);
  613.         break;
  614.     } else if (winPtr == focusPtr->focusWinPtr) {
  615.         /*
  616.          * The deleted window had the focus for its top-level:
  617.          * move the focus to the top-level itself.
  618.          */
  619.  
  620.         focusPtr->focusWinPtr = focusPtr->topLevelPtr;
  621.         if ((dispPtr->focusWinPtr == winPtr)
  622.             && !(focusPtr->topLevelPtr->flags & TK_ALREADY_DEAD)) {
  623.         if (focusDebug) {
  624.             printf("forwarding focus to %s after %s died\n",
  625.                 focusPtr->topLevelPtr->pathName, winPtr->pathName);
  626.         }
  627.         GenerateFocusEvents(dispPtr->focusWinPtr,
  628.             focusPtr->topLevelPtr);
  629.         dispPtr->focusWinPtr = focusPtr->topLevelPtr;
  630.         }
  631.         break;
  632.     }
  633.     }
  634.  
  635.     if (winPtr->mainPtr->lastFocusPtr == winPtr) {
  636.     winPtr->mainPtr->lastFocusPtr = NULL;
  637.     }
  638. }
  639.  
  640. /*
  641.  *----------------------------------------------------------------------
  642.  *
  643.  * GenerateFocusEvents --
  644.  *
  645.  *    This procedure is called to create FocusIn and FocusOut events to
  646.  *    move the input focus from one window to another.
  647.  *
  648.  * Results:
  649.  *    None.
  650.  *
  651.  * Side effects:
  652.  *    FocusIn and FocusOut events are generated.
  653.  *
  654.  *----------------------------------------------------------------------
  655.  */
  656.  
  657. static void
  658. GenerateFocusEvents(sourcePtr, destPtr)
  659.     TkWindow *sourcePtr;    /* Window that used to have the focus (may
  660.                  * be NULL). */
  661.     TkWindow *destPtr;        /* New window to have the focus (may be
  662.                  * NULL). */
  663.  
  664. {
  665.     XEvent event;
  666.     TkWindow *winPtr;
  667.  
  668.     winPtr = sourcePtr;
  669.     if (winPtr == NULL) {
  670.     winPtr = destPtr;
  671.     if (winPtr == NULL) {
  672.         return;
  673.     }
  674.     }
  675.  
  676.     event.xfocus.serial = LastKnownRequestProcessed(winPtr->display);
  677.     event.xcrossing.send_event = GENERATED_EVENT_MAGIC;
  678.     event.xcrossing.display = winPtr->display;
  679.     TkInOutEvents(&event, sourcePtr, destPtr, FocusOut, FocusIn);
  680. }
  681.  
  682. /*
  683.  *----------------------------------------------------------------------
  684.  *
  685.  * ChangeXFocus --
  686.  *
  687.  *    This procedure is invoked to move the official X focus from
  688.  *    one top-level to another.  We do this when the application
  689.  *    changes the focus window from one top-level to another, in
  690.  *    order to notify the window manager so that it can highlight
  691.  *    the new focus top-level.
  692.  *
  693.  * Results:
  694.  *    None.
  695.  *
  696.  * Side effects:
  697.  *    The official X focus window changes;  the application's focus
  698.  *    window isn't changed by this procedure.
  699.  *
  700.  *----------------------------------------------------------------------
  701.  */
  702.  
  703. static void
  704. ChangeXFocus(topLevelPtr, force)
  705.     TkWindow *topLevelPtr;    /* Top-level window that is to receive
  706.                  * the X focus. */
  707.     int force;            /* Non-zero means claim the focus even
  708.                  * if it didn't originally belong to
  709.                  * topLevelPtr's application. */
  710. {
  711.     TkDisplay *dispPtr = topLevelPtr->dispPtr;
  712.     TkWindow *winPtr;
  713.     Window focusWindow;
  714.     int dummy;
  715.     Tk_ErrorHandler errHandler;
  716.  
  717.     /*
  718.      * If the focus was received implicitly, then there's no advantage
  719.      * in setting an explicit focus;  just return.
  720.      */
  721.  
  722.     if (dispPtr->implicitWinPtr != NULL) {
  723.     return;
  724.     }
  725.  
  726.     /*
  727.      * Check to make sure that the focus is still in one of the
  728.      * windows of this application.  Furthermore, grab the server
  729.      * to make sure that the focus doesn't change in the middle
  730.      * of this operation.
  731.      */
  732.  
  733.     if (!focusDebug) {
  734.     XGrabServer(dispPtr->display);
  735.     }
  736.     if (!force) {
  737.     XGetInputFocus(dispPtr->display, &focusWindow, &dummy);
  738.     winPtr = (TkWindow *) Tk_IdToWindow(dispPtr->display, focusWindow);
  739.     if ((winPtr == NULL) || (winPtr->mainPtr != topLevelPtr->mainPtr)) {
  740.         goto done;
  741.     }
  742.     }
  743.  
  744.     /*
  745.      * Tell X to change the focus.  Ignore errors that occur when changing
  746.      * the focus:  it is still possible that the window we're focussing
  747.      * to could have gotten unmapped, which will generate an error.
  748.      */
  749.  
  750.     errHandler = Tk_CreateErrorHandler(dispPtr->display, -1, -1, -1,
  751.         (Tk_ErrorProc *) NULL, (ClientData) NULL);
  752.     XSetInputFocus(dispPtr->display, topLevelPtr->window, RevertToParent,
  753.         CurrentTime);
  754.     Tk_DeleteErrorHandler(errHandler);
  755.     if (focusDebug) {
  756.     printf("Set X focus to %s\n", topLevelPtr->pathName);
  757.     }
  758.  
  759.     done:
  760.     if (!focusDebug) {
  761.     XUngrabServer(dispPtr->display);
  762.     }
  763. }
  764.  
  765. /*
  766.  *----------------------------------------------------------------------
  767.  *
  768.  * FocusMapProc --
  769.  *
  770.  *    This procedure is called as an event handler for StructureNotify
  771.  *    events, if a window receives the focus at a time when its
  772.  *    toplevel isn't mapped.  The procedure is needed because X
  773.  *    won't allow the focus to be set to an unmapped window;  we
  774.  *    detect when the toplevel is mapped and set the focus to it then.
  775.  *
  776.  * Results:
  777.  *    None.
  778.  *
  779.  * Side effects:
  780.  *    If this is a map event, the focus gets set to the toplevel
  781.  *    given by clientData.
  782.  *
  783.  *----------------------------------------------------------------------
  784.  */
  785.  
  786. static void
  787. FocusMapProc(clientData, eventPtr)
  788.     ClientData clientData;    /* Toplevel window. */
  789.     XEvent *eventPtr;        /* Information about event. */
  790. {
  791.     TkWindow *winPtr = (TkWindow *) clientData;
  792.     TkDisplay *dispPtr = winPtr->dispPtr;
  793.  
  794.     if (eventPtr->type == MapNotify) {
  795.     ChangeXFocus(winPtr, dispPtr->forceFocus);
  796.     Tk_DeleteEventHandler((Tk_Window) winPtr, StructureNotifyMask,
  797.         FocusMapProc, clientData);
  798.     dispPtr->focusOnMapPtr = NULL;
  799.     }
  800. }
  801.